package mylittleinvestigations;

import mylittleinvestigations.CaseInformation.CaseXmlWriter;
import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.CaseInformation.CaseXmlReader;
import java.io.IOException;
import java.util.*;
import mylittleinvestigations.CaseInformation.CaseParseException;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;

public class DialogCharacter
{
    private class OneTimeEmotion
    {
        private String transitionToEmotion;
        private int leadInTime;
        private String videoId;
        
        public String GetTransitionToEmotion() { return this.transitionToEmotion; }
        public int GetLeadInTime() { return this.leadInTime; }
        public String GetVideoId() { return this.videoId; }
        
        public OneTimeEmotion(String transitionToEmotion, int leadInTime, String videoId)
        {
            this.transitionToEmotion = transitionToEmotion;
            this.leadInTime = leadInTime;
            this.videoId = videoId;
        }
        
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("OneTimeEmotion");
            
            if (this.transitionToEmotion != null)
            {
                writer.WriteTextElement("TransitionToEmotion", this.transitionToEmotion);
            }
            
            if (this.leadInTime > 0)
            {
                writer.WriteIntElement("LeadInTime", this.leadInTime);
            }
            
            writer.WriteTextElement("VideoId", this.videoId);
            
            writer.EndElement();
        }
    }
    
    private HashMap<String, String> characterEmotionBaseSpriteIds;
    private HashMap<String, ArrayList<String>> characterEmotionEyeSpriteIds;
    private HashMap<String, ArrayList<String>> characterEmotionMouthSpriteIds;
    private HashMap<String, ArrayList<String>> characterEmotionForegroundLayerIds;
    private HashMap<String, OneTimeEmotion> characterOneTimeEmotions;

    private HashMap<String, Sprite> characterEmotionBaseSprites;
    private HashMap<String, ArrayList<Sprite>> characterEmotionEyeSprites;
    private HashMap<String, ArrayList<Sprite>> characterEmotionMouthSprites;
    private HashMap<String, ArrayList<Animation>> characterEmotionForegroundLayers;
    
    private int currentEyeFrame;
    private int msElapsedCurrentEyeFrame;
    private ArrayList<Integer> eyeFrameDurationList;
    
    private int currentMouthIndex;
    private ArrayList<Integer> mouthIndexOrder;

    public DialogCharacter(String id, String name)
    {
        this.id = id;
        this.name = name;
        
        this.characterEmotionBaseSpriteIds = new HashMap<String, String>();
        this.characterEmotionEyeSpriteIds = new HashMap<String, ArrayList<String>>();
        this.characterEmotionMouthSpriteIds = new HashMap<String, ArrayList<String>>();
        this.characterEmotionForegroundLayerIds = new HashMap<String, ArrayList<String>>();
        this.characterOneTimeEmotions = new HashMap<String, OneTimeEmotion>();
        
        this.characterEmotionBaseSprites = new HashMap<String, Sprite>();
        this.characterEmotionEyeSprites = new HashMap<String, ArrayList<Sprite>>();
        this.characterEmotionMouthSprites = new HashMap<String, ArrayList<Sprite>>();
        this.characterEmotionForegroundLayers = new HashMap<String, ArrayList<Animation>>();
        
        this.eyeFrameDurationList = new ArrayList<Integer>();
    }
    
    private String id;
    
    private String name;

    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
    
    private String defaultEmotionId;

    public String getDefaultEmotionId() { return this.defaultEmotionId; }
    public void setDefaultEmotionId(String defaultEmotion) { this.defaultEmotionId = defaultEmotion; }
    
    // Also add associated audio clips, too.
    
    private String presentCorrectEvidenceSpriteId;

    public String GetPresentCorrectEvidenceSpriteId() { return this.presentCorrectEvidenceSpriteId; }
    public void SetPresentCorrectEvidenceSpriteId(String presentCorrectEvidenceSpriteId) { this.presentCorrectEvidenceSpriteId = presentCorrectEvidenceSpriteId; }
    
    private String presentCorrectEvidenceSoundId;

    public String GetPresentCorrectEvidenceSoundId() { return this.presentCorrectEvidenceSoundId; }
    public void SetPresentCorrectEvidenceSoundId(String presentCorrectEvidenceSoundId) { this.presentCorrectEvidenceSoundId = presentCorrectEvidenceSoundId; }
    
    private String presentCorrectEvidencePromptedSoundId;
    
    public String GetPresentCorrectEvidencePromptedSoundId() { return this.presentCorrectEvidencePromptedSoundId; }
    public void SetPresentCorrectEvidencePromptedSoundId(String presentCorrectEvidencePromptedSoundId) { this.presentCorrectEvidencePromptedSoundId = presentCorrectEvidencePromptedSoundId; }
    
    private String presentIncorrectEvidenceSpriteId;

    public String GetPresentIncorrectEvidenceSpriteId() { return this.presentIncorrectEvidenceSpriteId; }
    public void SetPresentIncorrectEvidenceSpriteId(String presentIncorrectEvidenceSpriteId) { this.presentIncorrectEvidenceSpriteId = presentIncorrectEvidenceSpriteId; }
    
    private String presentIncorrectEvidenceSoundId;

    public String GetPresentIncorrectEvidenceSoundId() { return this.presentIncorrectEvidenceSoundId; }
    public void SetPresentIncorrectEvidenceSoundId(String presentIncorrectEvidenceSoundId) { this.presentIncorrectEvidenceSoundId = presentIncorrectEvidenceSoundId; }
    
    private String pressStatementSpriteId;

    public String GetPressStatementSpriteId() { return this.pressStatementSpriteId; }
    public void SetPressStatementSpriteId(String pressStatementSpriteId) { this.pressStatementSpriteId = pressStatementSpriteId; }
    
    private String pressStatementSoundId;

    public String GetPressStatementSoundId() { return this.pressStatementSoundId; }
    public void SetPressStatementSoundId(String pressStatementSoundId) { this.pressStatementSoundId = pressStatementSoundId; }
    
    private String interjectionSpriteId;

    public String GetInterjectionSpriteId() { return this.interjectionSpriteId; }
    public void SetInterjectionSpriteId(String interjectionSpriteId) { this.interjectionSpriteId = interjectionSpriteId; }
    
    private String interjectionSoundId;

    public String GetInterjectionSoundId() { return this.interjectionSoundId; }
    public void SetInterjectionSoundId(String interjectionSoundId) { this.interjectionSoundId = interjectionSoundId; }
    
    private String breakdownVideoId;

    public String GetBreakdownVideoId() { return this.breakdownVideoId; }
    public void SetBreakdownVideoId(String breakdownVideoId) { this.breakdownVideoId = breakdownVideoId; }
    
    private String healthBackgroundSpriteTextureId;

    public String GetHealthBackgroundSpriteTextureId() { return this.healthBackgroundSpriteTextureId; }
    public void SetHealthBackgroundSpriteTextureId(String healthBackgroundSpriteTextureId) { this.healthBackgroundSpriteTextureId = healthBackgroundSpriteTextureId; }
    
    private String entranceForegroundSpriteTextureId;

    public String GetEntranceForegroundSpriteTextureId() { return this.entranceForegroundSpriteTextureId; }
    public void SetEntranceForegroundSpriteTextureId(String entranceForegroundSpriteTextureId) { this.entranceForegroundSpriteTextureId = entranceForegroundSpriteTextureId; }
    
    private Color backgroundColor;

    public Color GetBackgroundColor() { return this.backgroundColor; }
    public void SetBackgroundColor(Color backgroundColor) { this.backgroundColor = backgroundColor; }

    public void AddBaseSpriteForEmotion(String emotionId, String textureId)
    {
        this.characterEmotionBaseSpriteIds.put(emotionId, textureId);
    }

    public void AddEyeSpriteForEmotion(String emotionId, String textureId)
    {
        ArrayList<String> eyeTextureIdList = null;
        
        if (this.characterEmotionEyeSpriteIds.containsKey(emotionId))
        {
            eyeTextureIdList = this.characterEmotionEyeSpriteIds.get(emotionId);
        }
        else
        {
            eyeTextureIdList = new ArrayList<String>();
            this.characterEmotionEyeSpriteIds.put(emotionId, eyeTextureIdList);
        }
        
        eyeTextureIdList.add(textureId);
    }

    public void AddMouthSpriteForEmotion(String emotionId, String textureId)
    {
        ArrayList<String> mouthTextureIdList = null;
        
        if (this.characterEmotionMouthSpriteIds.containsKey(emotionId))
        {
            mouthTextureIdList = this.characterEmotionMouthSpriteIds.get(emotionId);
        }
        else
        {
            mouthTextureIdList = new ArrayList<String>();
            this.characterEmotionMouthSpriteIds.put(emotionId, mouthTextureIdList);
        }
        
        mouthTextureIdList.add(textureId);
    }
    
    public void AddForegroundLayerForEmotion(String emotionId, String animationId)
    {
        ArrayList<String> foregroundLayerIdList = null;
        
        if (this.characterEmotionForegroundLayerIds.containsKey(emotionId))
        {
            foregroundLayerIdList = this.characterEmotionForegroundLayerIds.get(emotionId);
        }
        else
        {
            foregroundLayerIdList = new ArrayList<String>();
            this.characterEmotionForegroundLayerIds.put(emotionId, foregroundLayerIdList);
        }
        
        foregroundLayerIdList.add(animationId);
    }
    
    public void AddOneTimeEmotion(String emotionId, String transitionToEmotion, int leadInTime, String videoId)
    {
        this.characterOneTimeEmotions.put(emotionId, new OneTimeEmotion(transitionToEmotion, leadInTime, videoId));
    }
    
    public void Update(GameContainer gc, int delta, String emotionId)
    {
        ArrayList<String> eyeSpriteIds = this.characterEmotionEyeSpriteIds.get(emotionId);
        
        if (eyeSpriteIds == null)
        {
            return;
        }
        
        if (this.eyeFrameDurationList.size() != eyeSpriteIds.size())
        {
            this.PopulateEyeFrameDurationList(emotionId);
        }
        
        this.msElapsedCurrentEyeFrame += delta;

        while (this.msElapsedCurrentEyeFrame > this.eyeFrameDurationList.get(this.currentEyeFrame))
        {
            this.msElapsedCurrentEyeFrame -= this.eyeFrameDurationList.get(this.currentEyeFrame);
            this.currentEyeFrame++;

            // If we've reached the end, then we'll wrap back around and get a
            // new random number representing the time until the next eye blink.
            if (this.currentEyeFrame >= eyeFrameDurationList.size())
            {
                this.currentEyeFrame = 0;
                this.eyeFrameDurationList.set(0, (int)(Math.random() * 2000) + 2000);
            }
        }
    }
    
    private void PopulateEyeFrameDurationList(String emotionId)
    {
        this.eyeFrameDurationList.clear();
        
        // We want the time until the next eye blink to be random, in order
        // for the blinks to appear natural rather than mechanical.
        this.eyeFrameDurationList.add((int)(Math.random() * 2000) + 2000);
        
        for (int i = 1; i < this.characterEmotionEyeSpriteIds.get(emotionId).size(); i++)
        {
            this.eyeFrameDurationList.add(75);
        }
    }
    
    public void Draw(GameContainer gc, Graphics g, String emotionId, boolean isTalking, boolean shouldChangeMouth, boolean isRightSide, int xOffset)
    {
        Sprite baseSprite = this.GetBaseSpriteForEmotion(emotionId);
        Sprite eyeSprite = this.GetEyeSpriteForEmotion(emotionId);
        Sprite mouthSprite = this.GetMouthSpriteForEmotion(emotionId, isTalking, shouldChangeMouth);
        
        if (baseSprite != null)
        {
            Vector2 position = new Vector2((isRightSide ? Settings.DisplayBounds.getX() - baseSprite.getWidth() : 0) + xOffset, Settings.DisplayBounds.getY() - Dialog.Height - baseSprite.getHeight());

            baseSprite.Draw(gc, g, position, Color.white, 1.0f, !isRightSide);

            if (eyeSprite != null)
            {
                eyeSprite.Draw(gc, g, position, Color.white, 1.0f, !isRightSide);
            }

            if (mouthSprite != null)
            {
                mouthSprite.Draw(gc, g, position, Color.white, 1.0f, !isRightSide);
            }
        }
    }

    public Sprite GetBaseSpriteForEmotion(String emotionId)
    {
        Sprite sprite = null;
        
        if (this.characterEmotionBaseSprites.containsKey(emotionId))
        {
            sprite = this.characterEmotionBaseSprites.get(emotionId);
        }
        else
        {
            sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(this.characterEmotionBaseSpriteIds.get(emotionId));
            this.characterEmotionBaseSprites.put(emotionId, sprite);
        }
        
        return sprite;
    }

    public Sprite GetEyeSpriteForEmotion(String emotionId)
    {
        ArrayList<Sprite> eyeSpriteList = null;
        
        if (this.characterEmotionEyeSprites.containsKey(emotionId))
        {
            eyeSpriteList = this.characterEmotionEyeSprites.get(emotionId);
        }
        else
        {
            ArrayList<String> eyeSpriteIdList = this.characterEmotionEyeSpriteIds.get(emotionId);
            
            if (eyeSpriteIdList == null || eyeSpriteIdList.isEmpty())
            {
                return null;
            }
            
            eyeSpriteList = new ArrayList<Sprite>();
            
            for (int i = 0; i < eyeSpriteIdList.size(); i++)
            {
                Sprite sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(this.characterEmotionEyeSpriteIds.get(emotionId).get(i));
                eyeSpriteList.add(sprite);
            }
            
            this.characterEmotionEyeSprites.put(emotionId, eyeSpriteList);
        }
        
        return eyeSpriteList.get(this.currentEyeFrame);
    }

    public Sprite GetMouthSpriteForEmotion(String emotionId, boolean isTalking, boolean shouldChangeMouth)
    {
        ArrayList<Sprite> mouthSpriteList = null;
        
        if (this.characterEmotionMouthSprites.containsKey(emotionId))
        {
            mouthSpriteList = this.characterEmotionMouthSprites.get(emotionId);
        }
        else
        {
            ArrayList<String> mouthSpriteIdList = this.characterEmotionMouthSpriteIds.get(emotionId);
            
            if (mouthSpriteIdList == null || mouthSpriteIdList.isEmpty())
            {
                return null;
            }
            
            mouthSpriteList = new ArrayList<Sprite>();
            
            for (int i = 0; i < mouthSpriteIdList.size(); i++)
            {
                Sprite sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(this.characterEmotionMouthSpriteIds.get(emotionId).get(i));
                mouthSpriteList.add(sprite);
            }
            
            this.characterEmotionMouthSprites.put(emotionId, mouthSpriteList);
        }
        
        // The closed mouth is always at position 0, so if we're not talking,
        // we want to return that one.  Otherwise, we want to return a random
        // frame in the sprite list.  This ensures that the talking animation
        // seems fluid rather than having it just be the same mouth positions
        // repeated over and over.
        int spriteIndex = 0;
        
        if (isTalking)
        {
            if (shouldChangeMouth)
            {
                if (this.mouthIndexOrder.isEmpty())
                {
                    for (int i = 0; i < mouthSpriteList.size(); i++)
                    {
                        this.mouthIndexOrder.add(i);
                    }
                    
                    Collections.shuffle(this.mouthIndexOrder);
                    
                    // We never want to begin with a shut mouth, so if
                    // a 0 is at the start, we'll move it to the second
                    // position.
                    if (this.mouthIndexOrder.get(0) == 0 && this.mouthIndexOrder.size() > 1)
                    {
                        this.mouthIndexOrder.set(0, this.mouthIndexOrder.get(1));
                        this.mouthIndexOrder.set(1, 0);
                    }
                }
                
                this.currentMouthIndex = this.mouthIndexOrder.remove(0);
            }
            
            spriteIndex = this.currentMouthIndex;
        }
        
        return mouthSpriteList.get(spriteIndex);
    }

    public void SaveToXml(CaseXmlWriter writer) throws IOException
    {
        writer.StartElement("DialogCharacter");
        writer.WriteTextElement("Name", this.name);
        
        writer.StartElement("CharacterEmotionBaseSpriteIds");
        String[] baseEmotionIds = this.characterEmotionBaseSpriteIds.keySet().toArray(new String[0]);
        
        for (int i = 0; i < baseEmotionIds.length; i++)
        {
            writer.StartElement("Entry");
            String emotionId = baseEmotionIds[i];
            writer.WriteTextElement("EmotionId", emotionId);
            Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + emotionId);
            writer.WriteSpriteId("SpriteId", this.characterEmotionBaseSpriteIds.get(emotionId));
            Case.getInstance().getSpriteManager().EndGroup();
            writer.EndElement();
        }
        
        writer.EndElement();
        
        writer.StartElement("CharacterEmotionEyeSpriteIds");
        String[] eyeEmotionIds = this.characterEmotionEyeSpriteIds.keySet().toArray(new String[0]);
        
        for (int i = 0; i < eyeEmotionIds.length; i++)
        {
            writer.StartElement("Entry");
            String emotionId = eyeEmotionIds[i];
            writer.WriteTextElement("EmotionId", emotionId);
            
            writer.StartElement("SpriteIds");
            ArrayList<String> spriteIdList = this.characterEmotionEyeSpriteIds.get(emotionId);
            
            Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + emotionId);
            
            for (int j = 0; j < spriteIdList.size(); j++)
            {
                writer.StartElement("Entry");
                writer.WriteSpriteId("SpriteId", spriteIdList.get(j));
                writer.EndElement();
            }
            
            Case.getInstance().getSpriteManager().EndGroup();
            
            writer.EndElement();
            writer.EndElement();
        }
        
        writer.EndElement();
        
        writer.StartElement("CharacterEmotionMouthSpriteIds");
        String[] mouthEmotionIds = this.characterEmotionMouthSpriteIds.keySet().toArray(new String[0]);
        
        for (int i = 0; i < mouthEmotionIds.length; i++)
        {
            writer.StartElement("Entry");
            String emotionId = mouthEmotionIds[i];
            writer.WriteTextElement("EmotionId", emotionId);
            
            writer.StartElement("SpriteIds");
            ArrayList<String> spriteIdList = this.characterEmotionMouthSpriteIds.get(emotionId);
            
            Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + emotionId);
            
            for (int j = 0; j < spriteIdList.size(); j++)
            {
                writer.StartElement("Entry");
                writer.WriteSpriteId("SpriteId", spriteIdList.get(j));
                writer.EndElement();
            }
            
            Case.getInstance().getSpriteManager().EndGroup();
            
            writer.EndElement();
            writer.EndElement();
        }
        
        writer.EndElement();
        
        writer.StartElement("CharacterEmotionForegroundLayerIds");
        String[] foregroundLayerIds = this.characterEmotionForegroundLayerIds.keySet().toArray(new String[0]);
        
        for (int i = 0; i < foregroundLayerIds.length; i++)
        {
            writer.StartElement("Entry");
            String emotionId = foregroundLayerIds[i];
            writer.WriteTextElement("EmotionId", emotionId);
            
            writer.StartElement("AnimationIds");
            ArrayList<String> animationIdList = this.characterEmotionForegroundLayerIds.get(emotionId);
            
            Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + emotionId);
            
            for (int j = 0; j < animationIdList.size(); j++)
            {
                writer.StartElement("Entry");
                writer.WriteTextElement("AnimationId", animationIdList.get(j));
                writer.EndElement();
            }
            
            Case.getInstance().getSpriteManager().EndGroup();
            
            writer.EndElement();
            writer.EndElement();
        }
        
        writer.EndElement();
        
        writer.StartElement("CharacterOneTimeEmotions");
        String[] emotionIds = this.characterOneTimeEmotions.keySet().toArray(new String[0]);
        
        for (int i = 0; i < emotionIds.length; i++)
        {
            writer.StartElement("Entry");
            String emotionId = emotionIds[i];
            writer.WriteTextElement("EmotionId", emotionId);
            
            writer.StartElement("OneTimeEmotion");
            this.characterOneTimeEmotions.get(emotionId).SaveToXml(writer);
            writer.EndElement();
            
            writer.EndElement();
        }
        
        writer.EndElement();
        
        writer.WriteTextElement("DefaultEmotionId", this.defaultEmotionId);
            
        Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + "Interjections");
        
        if (this.presentCorrectEvidenceSpriteId != null)
        {
            writer.WriteSpriteId("PresentCorrectEvidenceSpriteId", this.presentCorrectEvidenceSpriteId);
        }
        
        if (this.presentCorrectEvidenceSoundId != null)
        {
            writer.WriteTextElement("PresentCorrectEvidenceSoundId", this.presentCorrectEvidenceSoundId);
        }
        
        if (this.presentCorrectEvidencePromptedSoundId != null)
        {
            writer.WriteTextElement("PresentCorrectEvidencePromptedSoundId", this.presentCorrectEvidencePromptedSoundId);
        }

        if (this.presentIncorrectEvidenceSpriteId != null)
        {
            writer.WriteSpriteId("PresentIncorrectEvidenceSpriteId", this.presentIncorrectEvidenceSpriteId);
        }

        if (this.presentIncorrectEvidenceSoundId != null)
        {
            writer.WriteTextElement("PresentIncorrectEvidenceSoundId", this.presentIncorrectEvidenceSoundId);
        }

        if (this.pressStatementSpriteId != null)
        {
            writer.WriteSpriteId("PressStatementSpriteId", this.pressStatementSpriteId);
        }

        if (this.pressStatementSoundId != null)
        {
            writer.WriteTextElement("PressStatementSoundId", this.pressStatementSoundId);
        }

        if (this.interjectionSpriteId != null)
        {
            writer.WriteSpriteId("InterjectionSpriteId", this.interjectionSpriteId);
        }

        if (this.interjectionSoundId != null)
        {
            writer.WriteTextElement("InterjectionSoundId", this.interjectionSoundId);
        }
            
        Case.getInstance().getSpriteManager().EndGroup();
        
        if (this.breakdownVideoId != null)
        {
            writer.WriteTextElement("BreakdownVideoId", this.breakdownVideoId);
        }
            
        Case.getInstance().getSpriteManager().StartGroup("DialogCharacter" + this.id + "ConfrontationEntrance");
        
        if (this.healthBackgroundSpriteTextureId != null)
        {
            writer.WriteSpriteId("HealthBackgroundSpriteId", this.healthBackgroundSpriteTextureId);
        }
        
        if (this.entranceForegroundSpriteTextureId != null)
        {
            writer.WriteSpriteId("EntranceForegroundSpriteId", this.entranceForegroundSpriteTextureId);
            Case.getInstance().getSpriteManager().SaveSpriteIdToCurrentGroup(this.entranceForegroundSpriteTextureId + "White");
        }
        
        if (this.backgroundColor != null)
        {
            writer.StartElement("BackgroundColor");
            writer.WriteColorElement(this.backgroundColor);
            writer.EndElement();
        }
            
        Case.getInstance().getSpriteManager().EndGroup();
        
        writer.EndElement();
    }

    public DialogCharacter(CaseXmlReader reader)
    {
        this.currentMouthIndex = 0;
        this.mouthIndexOrder = new ArrayList<Integer>();
        
        this.characterEmotionBaseSpriteIds = new HashMap<String, String>();
        this.characterEmotionEyeSpriteIds = new HashMap<String, ArrayList<String>>();
        this.characterEmotionMouthSpriteIds = new HashMap<String, ArrayList<String>>();
        
        this.characterEmotionBaseSprites = new HashMap<String, Sprite>();
        this.characterEmotionEyeSprites = new HashMap<String, ArrayList<Sprite>>();
        this.characterEmotionMouthSprites = new HashMap<String, ArrayList<Sprite>>();
        
        this.eyeFrameDurationList = new ArrayList<Integer>();
        
        reader.StartElement("DialogCharacter");
        this.name = reader.ReadTextElement("Name");
        
        reader.StartElement("CharacterEmotionBaseSpriteIds");
        reader.StartList("Entry");
        
        while (reader.MoveToNextListItem())
        {
            String emotionId = reader.ReadTextElement("EmotionId");
            String spriteId = reader.ReadTextElement("SpriteId");
            
            this.characterEmotionBaseSpriteIds.put(emotionId , spriteId);
        }
        
        reader.EndElement();
        
        reader.StartElement("CharacterEmotionEyeSpriteIds");
        reader.StartList("Entry");
        
        while (reader.MoveToNextListItem())
        {
            String emotionId = reader.ReadTextElement("EmotionId");
            ArrayList<String> spriteIdList = new ArrayList<String>();
            
            reader.StartElement("SpriteIds");
            reader.StartList("Entry");
            
            while (reader.MoveToNextListItem())
            {
                spriteIdList.add(reader.ReadTextElement("SpriteId"));
            }
            
            reader.EndElement();
            
            this.characterEmotionEyeSpriteIds.put(emotionId, spriteIdList);
        }
        
        reader.EndElement();
        
        reader.StartElement("CharacterEmotionMouthSpriteIds");
        reader.StartList("Entry");
        
        while (reader.MoveToNextListItem())
        {
            String emotionId = reader.ReadTextElement("EmotionId");
            ArrayList<String> spriteIdList = new ArrayList<String>();
            
            reader.StartElement("SpriteIds");
            reader.StartList("Entry");
            
            while (reader.MoveToNextListItem())
            {
                spriteIdList.add(reader.ReadTextElement("SpriteId"));
            }
            
            reader.EndElement();
            
            this.characterEmotionMouthSpriteIds.put(emotionId, spriteIdList);
        }
        
        reader.EndElement();
        
        reader.EndElement();
    }

    private static boolean EyeFrameDurationListsAreEqual(ArrayList<Integer> eyeFrameDurationList1, ArrayList<Integer> eyeFrameDurationList2)
    {
        if (eyeFrameDurationList1.size() != eyeFrameDurationList2.size())
        {
            return false;
        }
        
        for (int i = 0; i < eyeFrameDurationList1.size(); i++)
        {
            if (!eyeFrameDurationList1.get(i).equals(eyeFrameDurationList2.get(i)))
            {
                return false;
            }
        }
        
        return true;
    }

    public void markChildSprites(String markParent, String emotionId) throws CaseParseException
    {
        if (this.characterEmotionBaseSpriteIds.containsKey(emotionId))
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.characterEmotionBaseSpriteIds.get(emotionId)).markChildSprites(markParent);
        }
        else if (this.characterOneTimeEmotions.containsKey(emotionId))
        {
            OneTimeEmotion oneTimeEmotion = this.characterOneTimeEmotions.get(emotionId);
            Case.getInstance().getAnimationManager().GetVideoFromId(oneTimeEmotion.GetVideoId()).markChildSprites(markParent);
            
            if (!Utils.isNullOrEmpty(oneTimeEmotion.GetTransitionToEmotion()))
            {
                this.markChildSprites(markParent, oneTimeEmotion.GetTransitionToEmotion());
            }
            
            return;
        }
        else
        {
            throw new CaseParseException("Character '" + this.id + "' has no emotion '" + emotionId + "'");
        }
        
        if (this.characterEmotionEyeSpriteIds.containsKey(emotionId))
        {
            for (String s : this.characterEmotionEyeSpriteIds.get(emotionId))
            {
                Sprite sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(s);
                sprite.markChildSprites(markParent);
            }
        }
        
        if (this.characterEmotionMouthSpriteIds.containsKey(emotionId))
        {
            for (String s : this.characterEmotionMouthSpriteIds.get(emotionId))
            {
                Sprite sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(s);
                sprite.markChildSprites(markParent);
            }
        }
        
        if (this.characterEmotionForegroundLayerIds.containsKey(emotionId))
        {
            for (String s : this.characterEmotionForegroundLayerIds.get(emotionId))
            {
                Animation animation = Case.getInstance().getAnimationManager().GetAnimationFromId(s);
                animation.markChildSprites(markParent);
            }
        }
    }

    public void markInterjectionChildSprites(String markParent)
    {
        if (this.presentCorrectEvidenceSpriteId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.presentCorrectEvidenceSpriteId).markChildSprites(markParent);
        }
        
        if (this.presentIncorrectEvidenceSpriteId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.presentIncorrectEvidenceSpriteId).markChildSprites(markParent);
        }
        
        if (this.pressStatementSpriteId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.pressStatementSpriteId).markChildSprites(markParent);
        }
        
        if (this.interjectionSpriteId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.interjectionSpriteId).markChildSprites(markParent);
        }
    }

    public void markConfrontationChildSprites(String markParent)
    {
        if (this.healthBackgroundSpriteTextureId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.healthBackgroundSpriteTextureId).markChildSprites(markParent);
        }
        
        if (this.entranceForegroundSpriteTextureId != null)
        {
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.entranceForegroundSpriteTextureId).markChildSprites(markParent);
            Case.getInstance().getSpriteManager().GetSpriteFromId(this.entranceForegroundSpriteTextureId + "White").markChildSprites(markParent);
        }
    }
    
    public void markBreakdownChildSprites(String markParent)
    {
        Video video = Case.getInstance().getAnimationManager().GetVideoFromId(this.breakdownVideoId);
        video.markChildSprites(markParent);
    }
}

